using UnityEngine;

public class OBBIndicator : IAttackIndicator
{
    private Vector3 m_HalfExtents = Vector3.one;
    private Quaternion m_Rotation = Quaternion.identity;
    private float m_FillAmount = 1f;
    private Color m_FillColor = Color.white;
    private Vector3 m_FillDirection = Vector3.forward;
    private Material m_MaterialInstance;

    private GameObject m_IndicatorObject;
    private MeshFilter m_MeshFilter;
    private MeshRenderer m_MeshRenderer;

    private bool m_IsDestroyed = false;
    private bool m_IsUpdating = false;

    public Vector3 HalfExtents
    {
        get => m_HalfExtents;
        set => m_HalfExtents = new Vector3(
            Mathf.Clamp(value.x, 0.1f, 20f),
            Mathf.Clamp(value.y, 0.1f, 20f),
            Mathf.Clamp(value.z, 0.1f, 20f));
    }

    public Quaternion Rotation
    {
        get => m_Rotation;
        set => m_Rotation = value;
    }

    public Vector3 RotationEuler
    {
        get => m_Rotation.eulerAngles;
        set => m_Rotation = Quaternion.Euler(value);
    }

    public void Initialize(GameObject parent, Material material)
    {
        m_IndicatorObject = new GameObject("OBBIndicator");
        m_IndicatorObject.AddComponent<RectTransform>();
        m_IndicatorObject.transform.SetParent(parent.transform, false);
        m_IndicatorObject.transform.localPosition = Vector3.zero;
        m_IndicatorObject.transform.localRotation = Quaternion.identity;

        m_MeshFilter = m_IndicatorObject.AddComponent<MeshFilter>();
        m_MeshRenderer = m_IndicatorObject.AddComponent<MeshRenderer>();

#if UNITY_EDITOR
        if (!Application.isPlaying)
        {
            m_MeshRenderer.sharedMaterial = material;
            m_MaterialInstance = m_MeshRenderer.sharedMaterial;
        }
        else
#endif
        {
            m_MaterialInstance = new Material(material);
            m_MeshRenderer.material = m_MaterialInstance;
        }

        m_MeshRenderer.sortingOrder = 3000;
        m_IsDestroyed = false;
        UpdateMesh();
    }

    public void UpdateParams()
    {
        if (m_IsDestroyed || m_IsUpdating) return;
        m_IsUpdating = true;
        try { UpdateMesh(); } finally { m_IsUpdating = false; }
    }

    public void SetFillAmount(float fillAmount)
    {
        m_FillAmount = Mathf.Clamp01(fillAmount);
        if (!m_IsDestroyed && !m_IsUpdating) UpdateMesh();
    }

    public void SetFillColor(Color color)
    {
        m_FillColor = color;
        if (m_MaterialInstance != null && !m_IsDestroyed)
        {
#if UNITY_EDITOR
            if (!Application.isPlaying) m_MeshRenderer.sharedMaterial.color = m_FillColor;
            else
#endif
                m_MaterialInstance.color = m_FillColor;
        }
    }

    public void SetFillDirection(Vector3 direction)
    {
        m_FillDirection = direction.normalized;
        if (!m_IsDestroyed && !m_IsUpdating) UpdateMesh();
    }

    private void UpdateMesh()
    {
        if (m_MeshFilter == null || m_IndicatorObject == null || m_IsDestroyed) return;
        if (m_FillAmount <= 0.01f) { m_MeshFilter.mesh = null; return; }

        Vector3 center = Vector3.zero;
        Vector3[] vertices = GetBoxVertices(center, m_HalfExtents, m_Rotation);
        Vector3 fillDir = m_Rotation * m_FillDirection;

        Vector3[] filledVertices = new Vector3[8];
        for (int i = 0; i < 8; i++)
        {
            Vector3 v = vertices[i];
            Vector3 dirToCenter = (v - center).normalized;
            float projection = Vector3.Dot(dirToCenter, fillDir);

            if (projection > 0)
            {
                float dist = (v - center).magnitude;
                float maxDist = dist * m_FillAmount;
                if (dist > maxDist) v = center + dirToCenter * maxDist;
            }
            filledVertices[i] = v;
        }

        int[] triangles = {
            0,2,1, 0,3,2, 4,5,6, 4,6,7,
            0,4,7, 0,7,3, 1,2,6, 1,6,5,
            0,1,5, 0,5,4, 3,7,6, 3,6,2
        };

        Vector2[] uvs = new Vector2[8];
        for (int i = 0; i < 8; i++)
            uvs[i] = new Vector2(
                filledVertices[i].x / (m_HalfExtents.x * 2) + 0.5f,
                filledVertices[i].y / (m_HalfExtents.y * 2) + 0.5f);

        Mesh mesh = new Mesh();
        mesh.vertices = filledVertices;
        mesh.triangles = triangles;
        mesh.uv = uvs;
        mesh.RecalculateNormals();
        mesh.RecalculateBounds();
        m_MeshFilter.mesh = mesh;
    }

    private Vector3[] GetBoxVertices(Vector3 c, Vector3 h, Quaternion r)
    {
        Vector3[] v = new Vector3[8];
        Vector3[] dirs = {
            new(-h.x, -h.y, -h.z), new( h.x, -h.y, -h.z),
            new( h.x,  h.y, -h.z), new(-h.x,  h.y, -h.z),
            new(-h.x, -h.y,  h.z), new( h.x, -h.y,  h.z),
            new( h.x,  h.y,  h.z), new(-h.x,  h.y,  h.z)
        };
        for (int i = 0; i < 8; i++) v[i] = c + r * dirs[i];
        return v;
    }

    public void Show() { if (m_IndicatorObject != null && !m_IsDestroyed) m_IndicatorObject.SetActive(true); }
    public void Hide() { if (m_IndicatorObject != null && !m_IsDestroyed) m_IndicatorObject.SetActive(false); }

    public void CleanUp()
    {
        if (m_IndicatorObject != null)
        {
            if (Application.isPlaying) Object.Destroy(m_IndicatorObject);
            else Object.DestroyImmediate(m_IndicatorObject);
            m_IsDestroyed = true;
        }
    }
}
